/*
 * nmatc.c
 *
 *  Created on: Mar 31, 2009
 *  Author: Marco Luisi
 */

#include "nmat.h" // vedere cosa spostarci
//#include "nmat_protocol.h"
#include "sysmacro.h"
#include "matrix.h" // chiedere
#include <fcntl.h> //serve nel client?
#include <ctype.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

#define FOREVER for(;;) // non serve qui?
#define N 255 //darne un valore giusto
//da capire quali globali e quali no.. PER TUTTI
char *username;
char *login;
char *buffer;
char *to_server;
char *from_server;
char input[N]; // oppure mallocarlo ogni volta?
matrix **mats; //stabilire se qui solo i nomi delle matrici
char **ctokens; // capire dove metterla

char *cmdline;

int pid;
int servpipe; //rinomniare magari
int in;
int out;
int auth;
/*
 *  uniformare i nomi delle pipe in tutti i sorgenti
 */
void menu();
void msgprint(msg);
//int parse(void);
int parse(); // da sistemare i parametri
int cmdcheck(int, char**);
void msetup(int, int, char**, msg*);//fare che restituisca il msg?
int send(msg*, int);
char** tokenizer(char*, int*);
double* tokenizer2(char*);

int list();


void term_handler(int);
void int_handler(int);
void cleanup(void);

//GESTIRE SIGPIPE!!!!!!!

//cosa strana:
//se muore worker, prende due comandi poi muore anche client
int main(int argc, char **argv) { // o *argv[]

	struct sigaction term_action;
	bzero(&term_action, sizeof(term_action));
	term_action.sa_handler = term_handler;
	sigaction(SIGTERM, &term_action, NULL);

	struct sigaction int_action;
	bzero(&int_action, sizeof(int_action));
	int_action.sa_handler = int_handler;
	sigaction(SIGINT, &int_action, NULL);

	int aux = 0;
	//GUARDARE ESERCIZIO SHELL PER lo SWITCH COMANDI
	if (argc != 2) {
		WRITELN("NMAT Client: Errore! Fornire il nome utente!");
		return -1;
	}
	//Se l’utente non viene autenticato,
	//esecuzione di nmatc termina visualizzando
	// un messaggio di errore sullo stderror
	//!!! farne un init() !!!
	pid = getpid();
	username = argv[1];
	login = malloc(30 * sizeof(char));// basta anche meno capire...
	to_server = malloc(30 * sizeof(char));
	from_server = malloc(30 * sizeof(char)); // magari fare na macro per le malloc delle stringhe

	sprintf(to_server, "%s%d", "clientserver", pid);
	WRITE("Utente ");
	WRITE(username);
	WRITELN(" benvenuto nel sistema NMAT!");
	WRITELN("Collegamento al server in corso...");

	//attenzione che va avanti se c'è la pipe rimasta da un crash..
	if ((servpipe = open("clientserver", O_WRONLY)) < 0) { //usare IFERROR
		WRITELN("Errore nell'aprire la la pipe clientserver");
		return -1; //clenup?
	}
	perror("open clientserver");
	aux = sprintf(login, "%d%s%s", pid, " CONNECT ", username);//ritorna il numero caratteri
	WRITELN(login);
	//due volte aux etico?
	aux = write(servpipe, login, aux); //scrive se c'è la pipe ma non il server in ascolto
	//free(&aux); //tanto per?
	close(servpipe); // non serve più
	perror("close servpipe");
	sprintf(from_server, "%s%d", "serverclient", pid);
	mkfifo(from_server, 0644); //creata prima del login
	perror("mkfifo from server");
	//chiedere se ok!!
	in = open(from_server, O_RDONLY);//senza nonblock si blocca perchè viene aperta solo a ricezione dal server
	perror("open from_server");
	printf("in = %d\n", in);

	WRITELN("in attesa di autenticazione");
	//WRITE("pausa 3 secondi tanto per..");
	//sleep(3);
	buffer = malloc(30 * sizeof(char));
	read(in, buffer, 30);
	printf("letto dopo login: !%s!\n", buffer);
	char *auxl;
	auxl = malloc(5 * sizeof(char));
	auxl = strtok(buffer, "LOGIN ");
	printf("auth=%s\n", auxl); //attenzione!! da null a volte
	int login;
	login = atoi(auxl);

	switch (login) { // fare una connect()?

	case 1: {
		WRITELN("Errore: nome utente non valido!");
		exit(1); //cleanup?
		break; //inutili?
	}

	case -1: {
		WRITELN("Errore: utente già connesso al sistema!")
		exit(1); //cleanup?
		break; //inutili?
	}
		//ma vedere se c'è da ripulire cmq!
	}

	WRITE("Utente ");
	WRITE(username);
	WRITELN(" autenticato!");

	printf("apro la pipe !%s!\n", to_server);
	int checkpipe;
	for (checkpipe = 0; checkpipe < 5; checkpipe++) {
		out = open(to_server, O_WRONLY); //controllo errore o forse già il controllo con gli if basta?
		printf("ciclo di checkpipe # %d, out = %d\n", checkpipe, out);
		if ( out>0 ) { //usare IFERROR
				printf("out = %d pipe trovata\n",out);
				break;
			}

		if(checkpipe == 4) {
			printf("pipe non trovata.. esco\n");
			exit(-1); //cleanup..
		}
		printf("sleep 1 sec\n");
		sleep(1); //poi provare nanosleep
		//nanosleep();
	}

	menu();
	printf("FUORI MENU'!!!\n");
	close(in);
	close(out);
	unlink(from_server);
	perror("unlink from server");
	return 0; // temporaneo
	// RICORDARE DI METTERE EXIT_SUCCESS; etc
}

void menu(int out) { // solo per la stampa del menu?
	WRITELN("inizio menù!");
	int run = 1; //nome migliore?

	while (run) {
		printf("in menu chiamo parse()\n");
		run = parse(out); //direttamente nel while?
		printf("uscita da parse()\n");
		if (run == _list) {
			list();
		}
	}
	cleanup();
} // fine menù

//rimane da vedere che capita alla seconda chiamata

int parse() { //forse non serva sia funz di tipo int
	int rd;
	int i = 0;
	int input_len = 0;
	char **tkns;
	int cmd_code;
	msg m;
	//legge la riga poi sistemare..
	while ((rd = read(0, input, N)) > 0) { // fare una funzione per raccattare l'input?
		//perror("read input"); // perchè dice sempre no such file??
		input_len = +rd;
		input[rd - 1] = '\0'; // non capisco perchè serve, se serve..
	}
	printf("in parse letti %d bytes\n",input_len);
	input[rd - 1] = '\0'; // giustificarlo
	printf("parse() parsing: !%s! -- letti %d byte -- strlen = %d\n", input,
			input_len, (int) strlen(input));

	tkns = tokenizer(input, &i); //spezzo la linea in tokens,
	// ora ho i token, array di stringhe.. cmd.. arg e numeri insieme
	// SISTEMAMI
	cmd_code = cmdcheck(i, tkns); //attenzione parte un segmentation qui
	// SISTEMAMI
	if (cmd_code != -1) {
		msetup(cmd_code, i, tkns, &m);
		printf(
				"parse()  ROBA IN MSG!codice = %d | num args = %d | num numbers = %d\n",
				m.cmdcode, m.argnum, m.nnum);
		send(&m, out);

		msgprint(m);
	}
	//prima cosa.. trovare il comando
	return cmd_code;
}

/*
 * il controllo sulla dim delle matrici dove farlo?
 * funz a parte per parsare i double
 */

int cmdcheck(int tnum, char** tkn) {// ofare uno while !null evtando il tnum?
	//dividere a seconda dei casi
	//prima i casi con comando singolo (display, exit, help)
	switch (tnum) {

	case 1: { //se 1 token [exit,display,help]
		if (strcmp(tkn[0], "exit") == 0)
			return _exi;
		if (strcmp(tkn[0], "help") == 0)
			return _help;
		if (strcmp(tkn[0], "list") == 0)
			return _list;
		else {
			return -1; // define? ;)
		}

	} // fine case 1

	case 2: { //se 2 token [print,save,load, o niu (cancellazione mat)]
		if (strcmp(tkn[1], "=") == 0) //cancellazione.
			return _new; // fare un codice apposito per questa che tanto cancella?
		if (strcmp(tkn[0], "print") == 0)
			return _print; // e se uno facesse "print new" andrebbe affanculo..
		if (strcmp(tkn[0], "save") == 0)
			return _save;
		if (strcmp(tkn[0], "load") == 0)
			return _load;
		else {
			return -1;
		}

	} // fine case 2

		//	case 3: { //forse solo se creo una matrice con 1 solo elemento?
		//		// forse non serve rientra nel caso default che tratta solo la niu
		//	}

	case 4: {

		if ((strcmp(tkn[1], "=") == 0) && (strcmp(tkn[2], "TRASP") == 0)) {
			return _TRASP;
		}
		//break; // ma dopo return?
	} //fine case 4


	case 5: { //se 5 token [op,trasp]
		if (strcmp(tkn[1], "=") == 0) { // o è una niu di una matrice

			if (strcmp(tkn[3], "+") == 0)
				return _add;
			if (strcmp(tkn[3], "-") == 0)
				return _sub;
			if (strcmp(tkn[3], "*") == 0)
				return _mul;
		} // tutti i casi con k = x op k

		else { // non c'è = quindi errore
			return -1;
		}

	} //fine case 5

	default: { //considerare lo 0? o considerarlo controllato prima?
		if (strcmp(tkn[1], "=") == 0) {
			return _new;
		} else {
			return -1;
		}
	}

	} // fine switch


} // fine cmdcheck


/*
 * ora tokenizer fa solo una cosa, spezza la riga in token
 */
// magari riusabile nel server per serializzare per l'invio??
char** tokenizer(char* line, int *num) { //forse aggiungere di passare il token da scartare.. per i ;
	int n = 0;
	int i;
	int allocated = 10;
	char **tokens;
	char skip[] = " \n"; // potrebbe essere da passare a seconda se poi si vuole skippare il ;
	tokens = malloc(allocated * (sizeof(char**)));
	perror("malloc allocated");
	tokens[n] = strtok(line, skip); //controllare la nullità?
	while (tokens[n] != NULL) { // mi par un troiaio
		n++;
		if (n == allocated) {
			allocated *= 2;
			printf("realloc di tokens\n");
			tokens = (char**) realloc(tokens, allocated * sizeof(char**)); //o sizeof char*?
			perror("realoc tokens");
		}
		tokens[n] = strtok(NULL, skip);
	}

	printf("tkn con %d stringhe\n", n);
	for (i = 0; i < n; i++) {
		printf("tkn[%d]= !%s!\n", i, tokens[i]);
	}
	printf("fine tokenizer()\n");

	*num = n;
	return tokens;
}

//o fare in modo di uniformare come sono disposti numeri e argomenti, spostando o togliendo il comando da tnks o..
//DILEMMA controllare i numeri nel client o nel server?????
// e invece che void fare che restituisce il msg?
void msetup(int cmd, int tkns_num, char** tkns, msg *cmd_msg) { // magari usarla in un puntatore a funzione come parametro per la send
	//dai tkns devo estrarre stringhe e numeri
	//ma tutto dipende dal comando
	//msg cmd_msg;
	//capire quando argnum è superfluo in base al comando
	cmd_msg->cmdcode = cmd; // ma questo vale per tutti a regola si può mettere
	// fuori dallo switch
	switch (cmd) { // codice ripetuto per exit help e list come risolvere?

	case _exi: {
		printf("MSETUP:CASE exit\n");
	}
		// con i case vuoti e il braek dopo funziona, ma bello?
	case _help: {
		printf("MSETUP:CASE help\n");
	}
	case _list: {
		printf("MSETUP:CASE list\n");
		cmd_msg->nnum = 0;
		cmd_msg->numbers = NULL; // unificare?
		cmd_msg->argnum = 0;
		cmd_msg->args = NULL;
		break;
	}
		// fine comandi senza parametri e numeri
	case _print: { //stessa cosa per print save e load...
		printf("MSETUP:CASE print\n");
		//		cmd_msg->nnum = 0;
		//		cmd_msg->numbers = NULL;
		//		cmd_msg->argnum = 1;
		//		cmd_msg->args = &tkns[1];
		//		break;
	}

	case _save: {
		printf("MSETUP:CASE save\n");
		//		cmd_msg->nnum = 0;
		//		cmd_msg->numbers = NULL;
		//		cmd_msg->argnum = 1;
		//		cmd_msg->args = &tkns[1];
		//		break;
	}

	case _load: {
		printf("MSETUP:CASE load\n");
		cmd_msg->nnum = 0;
		cmd_msg->numbers = NULL;
		cmd_msg->argnum = 1;
		cmd_msg->args = &tkns[1];
		break;
	}
		//ATANSIOOON PASSA ANCHE SE DOPO METTO STRINGHE INVECE DI NUMERI!!!!!
		// !!decidere se trattare separamente cancellazione e creazione di matrice!!
	case _new: { // prova per vedere se si può puntare al primo numero e poi scorrere
		//NON VA UNA SEGA
		printf("MSETUP:CASE new\n");
		//con argomento "troppo lungo" sbombarda qui
		cmd_msg->nnum = tkns_num - 2;
		printf("tknsnum=%d || cmdmsg-nnum=%d\n", tkns_num, cmd_msg->nnum);

		cmd_msg->argnum = 1; // sempre vero in questo caso
		cmd_msg->args = &tkns[0];
		printf("tkns0=%s\n", tkns[0]);
		printf("cmdmsg-arg=%s\n", cmd_msg->args[0]);

		//cmd_msg.arg_lenghts[0] = strlen(tkns[0]);

		printf("strelen tkns0=%d\n", (int) strlen(tkns[0]));

		cmd_msg->numbers = malloc((cmd_msg->nnum) * sizeof(int));

		perror("malloc di numbers");
		//cmd_msg->numbers = &tkns[2];
		int i;
		int y; //FUNGE MA E' UN TROIAIO
		for (i = 0; i < cmd_msg->nnum; i++) {
			y = i + 2;
			cmd_msg->numbers[i] = atoi(tkns[y]);
			perror("atoi tkns");
			printf("numbers[%d]=%d\n", i, cmd_msg->numbers[i]);
		}
		//printf("in msetup, cmd_msg->numbers[0]=INT %d\n",atoi(cmd_msg->numbers[0]));// quindi ok come int
		//printf("in msetup, cmd_msg->numbers[0]=STRING %s\n",cmd_msg->numbers[0]);
		printf("fine case new\n");
		break;
	}

	case _TRASP: {
		printf("MSETUP:CASE TRASP\n");
		cmd_msg->nnum = 0;
		cmd_msg->numbers = NULL;
		cmd_msg->argnum = 2;
		//char* temp[2]; // fa abbastanza schifo
		char** temp;
		temp = malloc(2 * sizeof(char*));
		temp[0] = tkns[0]; // sistemare tokenizer
		temp[1] = tkns[3]; // ci sarà un modo piu diretto?
		cmd_msg->args = &temp[0];
		printf("case trasp: cmdmsg-args0=%s\n", cmd_msg->args[0]);
		printf("case trasp: cmdmsg-args0=%s\n", cmd_msg->args[1]);
		break;
	}

	case _add: {
		printf("MSETUP:CASE add\n");
		break;
	}

	case _sub: {
		printf("MSETUP:CASE sub\n");
		break;
	}

	case _mul: {
		printf("MSETUP:CASE mul\n");
		break;
	}

	} // fine swith generale msetup

	//check del messaggio

	printf("fine msetup!\n");
} //fine msetup

//send invia codice, num di arg, num di arg e poi arg e numeri
int send(msg *m, int ch) { //segfault dopo parse() se sbomballa di la
	//IMPORTANTE: RICORDARSI CHE LE PERROR SERVONO SOLO DOPO UNA SC!!!!!!!!!!!
	int i;
	int aux =0;
	int arglen;
	// invio paremetri comuni a tutti i tipi di messaggio
	aux += write(ch, &(m->cmdcode), sizeof(int));
	aux += write(ch, &(m->argnum), sizeof(int));
	aux += write(ch, &(m->nnum), sizeof(int));
	//printf("SPEDITO cmd=%d ||argnum=%d ||nnum=%d\n",m->cmdcode,m->argnum,m->nnum);
	// invio degli argomenti string

	if (m->argnum) {
		for (i = 0; i < m->argnum; i++) {
			arglen = strlen(m->args[i]);
			aux += write(ch, &arglen, sizeof(int));
			aux += write(ch, m->args[i], arglen);
			printf("SPEDITO arg[%d]:%s\n", i, m->args[i]);
			//printf("SPEDITO &(arg[%d]):%s\n",i,&(m->args[i]));
			//printf("SPEDITO &arg[%d]:%s\n",i,&m->args[i]);
		}

	}

	if (m->nnum) {
		for (i = 0; i < m->nnum; i++) {
			aux += write(ch, &(m->numbers[i]), sizeof(int));
			//printf("SPEDITO %d\n",m->numbers[i]);
		}
	}
	printf("spediti %d bytes\n", aux);
	return 1; //asd
}

void msgprint(msg m) { //fare una print generica in un util.h o print.h !!!
	int i;
	printf("MSGPRINT:\n");
	if (&m != NULL) {
		printf("cmd = %d || argnum = %d || nnum = %d\n", m.cmdcode, m.argnum,
				m.nnum);
		if (m.argnum) {
			i = m.argnum;
			for (i = 0; i < m.argnum; i++) {
				printf("arg[%d]=%s\n", i, m.args[i]);
			}
		}
		if (m.nnum) {
			i = m.nnum;
			for (i = 0; i < m.nnum; i++) {
				printf("num[%d]=%d\n", i, m.numbers[i]);
			}
		}

	}
	printf("FINE MSGPRINT:\n");
}//fine msgprint


int list() {
	int len;
	int i;
	int mnum =0;
	printf("LIST!\n");
	printf("dentro list in = %d\n", in);
	read(in,&mnum,sizeof(int));
	printf("mnum=%d\n",mnum);
	perror("read mnum");
	for(i = 0; i< mnum; i++) {
		read(in, &len, sizeof(int));
		perror("read len");
		printf("len=%d\n",len);
		char* aux = malloc(len * sizeof(char));
		read(in,&aux,len);
		perror("read aux");
		printf("matrice %d, lung = %d: !%s!\n",i,len,aux);
		}
	return 1;
}


void term_handler(int signum) {
	printf("server in chiusura, impossibile effettuare altre operazioni\n");
	cleanup();
	//exit(0);
}

void int_handler(int signum) {
	close(in);
	close(out);
	printf("ricevuto sigint");
	exit(0);
}

void cleanup(void) {
	close(in);
	close(out);
	unlink(from_server);
	printf("from_server è  %s", from_server);
	perror("unlink from server");
	printf("cleanup");
	exit(0);
}
